﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.XPath;
using System.Xml.Linq;
using System.Web.Routing;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Numerics;
using System.Text;


namespace WebFabricator.org
{
    public static class Extensions
    {
        /// <summary>
        /// Get the TapestryNamespace attribute from the type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Namespace GetNamespace(this Type type)
        {
            return type.GetAttribute<NamespaceAttribute>().Namespace;
        }

        /// <summary>
        /// Get an attribute of type (T) from the type
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <returns></returns>
        public static T GetAttribute<T>(this Type type) where T : Attribute  
        {
            return type.GetCustomAttributes(typeof(T), true).FirstOrDefault() as T;
        }

        /// <summary>
        /// Definse a ForEach operator without having to cast tot List 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumerator"></param>
        /// <param name="action"></param>
        public static void ForEach<T>(this IEnumerable<T> enumerator, Action<T> action)
        {
            Array.ForEach(enumerator.ToArray(),action);
        }

        /// <summary>
        /// Extract the Target Adorner from the action it defines
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action"></param>
        /// <returns></returns>
        public static Delegate Adorner<T>(this Delegate action)
        {
            while (!(action.Target is IAdorner<T>))
            {
                action = ((Action<T>)action.Target.GetType().GetFields()[0].GetValue(action.Target));
            }
            return action;
        }
  
        /// <summary>
        /// Convert a byte[] to Base36 encoding
        /// </summary>
        /// <param name="toConvert"></param>
        /// <returns></returns>
        public static string ToBase36(this byte[] toConvert)
        {
            const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";//"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            //if (bigEndian) Array.Reverse(toConvert); // !BitConverter.IsLittleEndian might be an alternative
            BigInteger dividend = new BigInteger(toConvert);
            var builder = new StringBuilder();
            while (dividend != 0)
            {
                BigInteger remainder;
                dividend = BigInteger.DivRem(dividend, 36, out remainder);
                builder.Insert(0, alphabet[Math.Abs(((int)remainder))]);
            }
            return builder.ToString();
        }

        /// <summary>
        /// Convert a Base36 encoded string to a byte[]
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] FromBase36(this string value)
        {
            const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";//"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            var dividend = value.Reverse().Select((c, index) => alphabet.IndexOf(c) * BigInteger.Pow(36, index)).Aggregate((o, c) => o + c);
            return dividend.ToByteArray();
        }

        /// <summary>
        /// Base36 encode a string
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string ToBase36String(this string value)
        {
            return System.Text.ASCIIEncoding.ASCII.GetBytes(value).ToBase36();
        }

        /// <summary>
        /// Base36 decode a string
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string FromBase36String(this string value)
        {
            return System.Text.ASCIIEncoding.ASCII.GetString(value.FromBase36()).FromBase36String();
        }

        /// <summary>
        /// Determines if the type is an anonymous type created by the runtime
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsAnonymousType(this Type type)
        {
            if (type == null)
                throw new ArgumentNullException("type");

            // HACK: The only way to detect anonymous types right now.
            return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
                && type.IsGenericType && type.Name.Contains("AnonymousType")
                && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
                && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
        }

        /// <summary>
        /// String hashing function
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static UInt64 GetBigHashCode(this String value)
        {
            const ulong p = 1099511628211; //16777619;
            ulong hash = 2166136261;
            byte[] data = System.Text.ASCIIEncoding.ASCII.GetBytes(value);
            foreach (byte b in data)
                hash = (hash ^ b) * p;
            hash += hash << 13;
            hash ^= hash >> 7;
            hash += hash << 3;
            hash ^= hash >> 17;
            hash += hash << 5;
            return hash;
        }

        /// <summary>
        /// Uri hashing function
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static UInt64 GetBigHashCode(this Uri uri)
        {
            return uri.Normalize().ToString().GetBigHashCode();
            #region comment
            //const uint p = 16777619;
            //uint hash = 2166136261;
            //byte[] data = System.Text.ASCIIEncoding.ASCII.GetBytes(uri.Normalize().ToString());
            //foreach (byte b in data)
            //    hash = (hash ^ b) * p;
            //hash += hash << 13;
            //hash ^= hash >> 7;
            //hash += hash << 3;
            //hash ^= hash >> 17;
            //hash += hash << 5;
            //return hash;
            #endregion
        }

        /// <summary>
        /// Returns a url without query parameters
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static Uri Normalize(this Uri uri)
        {
            return new Uri((uri.GetLeftPart(UriPartial.Path) + uri.Fragment).ToLower());
        }

        //public static IWebFabricatorFactory<IWebFabric> Wrap(this IWebFabricatorFactory<IWebFabric> factory)
        //{
        //    if (factory.GetType().Name.EndsWith("Wrapper"))
        //        return factory;
        //    return new IWebFabricatorFactoryWrapper(factory);
        //}

        //public static IAdorner<IWebFabric> Wrap(this IAdorner<IWebFabric> fabricator)
        //{
        //    if (fabricator.GetType().Name.EndsWith("Wrapper"))
        //        return fabricator;
        //    return new TapestryAdornerWrapper(fabricator);
        //}


        public static T GetValue<T>(this System.Web.HttpApplication application) where T : class
        {
            return (T)application.Context.Items[typeof(T)];
        }

        public static void SetValue<T>(this System.Web.HttpApplication application, T value) where T : class
        {
            application.Context.Items[typeof(T)] = value;
        }

        public static void Publish(this EventFabric @event)
        {
            HttpContext.Current.ApplicationInstance.GetValue<EventSink<EventFabric>>().Publish(@event);
        }

       

    }

}